/***************************************************************************
 *   oboe.utils.MD2                                                        *
 *   Copyright (C) 2011 by Felipe Manga                                    *
 *   lufeboma@gmail.com                                                    *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/

package oboe.utils
{
	import flash.utils.ByteArray;
	import flash.utils.Endian;
	
	import oboe.core.Oboe;
	import oboe.math.Mesh3D;
	import oboe.math.Number2D;
	import oboe.math.Number3D;
	import oboe.math.Vertex3D;

	/**
	 * Reads MD2-format files from a bytearray and outputs a Mesh3D. 
	 * @author FelipeManga
	 * 
	 */
	public class MD2
	{
		public static const ANIM_STAND:Array = [0, 38];
		public static const ANIM_RUN:Array = [38, 45];
		
		private static var data : ByteArray;
		private static var head : Header;
		
		/**
		 * Called by the Parser class to read MD2 data. 
		 * @param _data the MD2 data
		 * @param ext the extension of the file (always md2).
		 * @return a mesh that can be rendered with an MD2Node.
		 * 
		 */
		public static function parse( _data : ByteArray, ext : String ) : Mesh3D {
			data = _data;
			if( data == null ) return null;
			
			head = new Header( data );
			
			readUVs();
			readIndices();
			readFrames();
			
			var uvlist:Vector.<Number> = new Vector.<Number>( head.frames[0].length*2 );
			var frames:Vector.< Vector.<Number3D> > = new Vector.<Vector.<Number3D>>( head.frames.length, true );
			var lf:int=frames.length;
			var frame:Vector.<Number3D>;
			var vframe:Vector.<Vertex3D> = head.frames[0];
			for( var f:int=0; f<lf; ++f )
			{
				frames[f] = new Vector.<Number3D>( head.frames[f].length );
			}
			
			frame = frames[0];
			var l:int=head.indices.length;
			var fl:int=frame.length-1;
			for( var i:int=0; i<l; ++i )
			{
				var uv:Number2D = head.uv[ i ];
				var vid:int = head.indices[i];
				var vert:Vertex3D = vframe[ vid ];
				if( !vert.uvl )
				{
					vert.uvl = new Hash();
					vert.uvl[ uv ] = i;
					uvlist[ vid*2 ] = uv.x;
					uvlist[ vid*2+1 ] = uv.y;
					for( f=0; f<lf; ++f )
					{
						frames[f][ vid ] = head.frames[f][ vid ];
					}
				}
				else if( uv in vert.uvl )
				{
					vid = head.indices[ vert.uvl[uv] ];
					for( f=0; f<lf; ++f )
					{
						frames[f][ vid ] = head.frames[f][ vid ];
					}
				}
				else
				{
					vert.uvl[ uv ] = i;
					head.indices[i] = fl;
					uvlist[ fl*2 ] = uv.x;
					uvlist[ fl*2+1 ] = uv.y;
					for( f=0; f<lf; ++f )
					{
						var fvert:Vertex3D = head.frames[f][ vid ];
						frames[f][fl] = new Number3D( fvert.x, fvert.y, fvert.z );
					}
					fl++;
				}
			}
			
			var mesh:Mesh3D = new Mesh3D( head.indices, frames, uvlist );
			
			data = null;
			head = null;
			return mesh;
		}
		
		private static function readUVs() : void {
			// s.seekg( head.offset_st, ios::beg );
			data.position = head.offset_st;
			for( var uvid:int=0; uvid<head.num_st; ++uvid ){
				//Twin< short > uv(0,0);
				head.uvlist[ uvid*2 ] = data.readShort() / head.skinwidth;
				//s.read( (char *)&uv.x, sizeof(short) );
				head.uvlist[ uvid*2+1] = data.readShort() / head.skinheight;
				//s.read( (char *)&uv.y, sizeof(short) );
				//r->uvs.push_back( Twin<float>( float( uv.x ) / float(head.skinwidth), float(uv.y) / float(head.skinheight) ) );
			}
		}
		
		private static function readIndices() : void {
			// s.seekg( head.offset_tris, ios::beg );
			data.position = head.offset_tris;
			for( var tid:int=0; tid<head.num_tris; ++tid ){
				// Triplet< unsigned short > t;
				head.indices[ tid*3+0 ] = (data.readShort() & 0xFFFF);
				head.indices[ tid*3+1 ] = (data.readShort() & 0xFFFF);
				head.indices[ tid*3+2 ] = (data.readShort() & 0xFFFF);
				// s.read( (char *)&t, sizeof(t) );
				// r->indices.push_back( t );
				
				// read UV indices
				var uvi:int = (data.readShort() & 0xFFFF) * 2;
				head.uv[ tid*3+0 ] = new Number2D( head.uvlist[ uvi ], head.uvlist[ uvi+1 ] );

				uvi = (data.readShort() & 0xFFFF) * 2;
				head.uv[ tid*3+1 ] = new Number2D( head.uvlist[ uvi ], head.uvlist[ uvi+1 ] );

				uvi = (data.readShort() & 0xFFFF) * 2;
				head.uv[ tid*3+2 ] = new Number2D( head.uvlist[ uvi ], head.uvlist[ uvi+1 ] );
				/*
				head.indices[ tid*6+3 ] = (data.readShort() & 0xFFFF) * 2;
				head.indices[ tid*6+4 ] = (data.readShort() & 0xFFFF) * 2;
				head.indices[ tid*6+5 ] = (data.readShort() & 0xFFFF) * 2;
				*/
			}
		}
		
		private static function readFrames() : void {
			data.position = head.offset_frames;
			// s.seekg( head.offset_frames, ios::beg );
			var ws : Number = Oboe.flags["SCALE_3D"];
			for( var fid:int=0; fid<head.num_frames; ++fid ){
				var scale : Number3D = new Number3D(0,0,0), 
					trans : Number3D = new Number3D(0,0,0);
				var name : String;
				var f:Vector.<Vertex3D> = head.frames[fid];
				//r->frames.push_back( f );
				
				scale.readFloat( data ).mul(ws);
				// s.read( (char *) &scale, sizeof(scale) );
				trans.readFloat( data ).mul(ws);
				// s.read( (char *) &trans, sizeof(trans) );
				data.position += 16; // skip name
				// s.read( name, 16 );
				// name[15] = 0;
				// cout << "reading frame " << name << " @" << s.tellg() << endl;
				for( var i:int=0; i<head.num_vertices; ++i ){
					var v : Vertex3D = new Vertex3D(0,0,0);
					// Triplet<unsigned char> d;
					// Vertex3D v;
					v.readByte( data );
					// s.read( (char *)&d, sizeof(d) );
					
					// v = d;
					v.mul3( scale ).add3( trans ).flipYZ();
					// v *= scale;
					// v += trans;
					// swap( v.z, v.y );
					
					f[ i ] = v;
					// f->push_back( v );
					
					data.readByte();
					// s.read( (char *)&d, 1 ); // read normal index
				}
			}
		}
	}
}
import flash.utils.ByteArray;
import flash.utils.Dictionary;
import flash.utils.Endian;

import oboe.math.Number2D;
import oboe.math.Number3D;
import oboe.math.Vertex3D;

internal class Header {
	public var  ident : int;                  /* magic number: "IDP2" */
	public var  version : int;                /* version: must be 8 */
	
	public var  skinwidth : int;              /* texture width */
	public var  skinheight : int;             /* texture height */
	
	public var  framesize : int;              /* size in bytes of a frame */
	
	public var  num_skins : int;              /* number of skins */
	public var  num_vertices : int;           /* number of vertices per frame */
	public var  num_st : int;                 /* number of texture coordinates */
	public var  num_tris : int;               /* number of triangles */
	public var  num_glcmds : int;             /* number of opengl commands */
	public var  num_frames : int;             /* number of frames */
	
	public var  offset_skins : int;           /* offset skin data */
	public var  offset_st : int;              /* offset texture coordinate data */
	public var  offset_tris : int;            /* offset triangle data */
	public var  offset_frames : int;          /* offset frame data */
	public var  offset_glcmds : int;          /* offset OpenGL command data */
	public var  offset_end : int;             /* offset end of file */
	
	public var indices:Vector.<int>;
	public var uvlist : Vector.< Number >;
	public var uv : Vector.<Number2D>;
	public var frames : Vector.< Vector.<Vertex3D> >;
	
	public function Header( data : ByteArray ) {
		data.position = 0;
		data.endian = Endian.LITTLE_ENDIAN;
		
		ident = data.readInt();					 /* magic number: "IDP2" */
		version = data.readInt();				 /* version: must be 8 */
		skinwidth = data.readInt();
		skinheight = data.readInt();
		framesize = data.readInt();              /* size in bytes of a frame */
		
		num_skins = data.readInt();              /* number of skins */
		num_vertices = data.readInt();           /* number of vertices per frame */
		num_st = data.readInt();                 /* number of texture coordinates */
		num_tris = data.readInt();               /* number of triangles */
		num_glcmds = data.readInt();             /* number of opengl commands */
		num_frames = data.readInt();             /* number of frames */
		
		offset_skins = data.readInt();           /* offset skin data */
		offset_st = data.readInt();              /* offset texture coordinate data */
		offset_tris = data.readInt();            /* offset triangle data */
		offset_frames = data.readInt();          /* offset frame data */
		offset_glcmds = data.readInt();          /* offset OpenGL command data */
		offset_end = data.readInt();             /* offset end of file */
		
		indices = new Vector.<int>( num_tris*3, true );
		uv = new Vector.<Number2D>( num_tris*3, true );
		frames = new Vector.< Vector.<Vertex3D> >( num_frames, true );
		uvlist = new Vector.<Number>( num_st*2, true );
		for( var i:int=0; i<num_frames; ++i )
		{
			frames[i] = new Vector.<Vertex3D>( num_vertices, true );
		}
	}
}
